﻿using GreenXi.Help.PLC;
using Helpers;
using S7.Net;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Windows.Forms;
using System.Xml.Linq;
using Util;

namespace GreenXi.Help.Plc
{
    public class PLC1
    {
        private static bool IsSetPLCMessageOpen = false;
        private static bool IsGetPLCMessageOpen = false;
        private static bool IsInitMessageOpen = false;

        #region PLC相关地址设置

        //private static readonly Dictionary<string, int> PlcAddressMap = LoadMap<int>("name");
        private static readonly ConcurrentDictionary<string, int> PlcAddressMap = LoadMap<int>("name");
        private static readonly ConcurrentDictionary<string, string> PlcTypeMap = LoadMap<string>("type");
        private static readonly ConcurrentDictionary<string, byte> PlcBitAdrMap = LoadMap<byte>("bitAdr");

        private static ConcurrentDictionary<string, T> LoadMap<T>(string mapName)
        {
            string configFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PLCAddress.config");
            XDocument doc = XDocument.Load(configFile);
            ConcurrentDictionary<string, T> map = new();
            foreach (XElement element in doc.Descendants("address"))
            {
                string logicalName = element.Attribute("name").Value;
                T value;
                switch (mapName)
                {
                    case "name":
                        value = GetValue<T>(element.Value);
                        map[logicalName] = value;
                        break;
                    case "type":
                        value = GetValue<T>(element.Attribute("type").Value);
                        map[logicalName] = value;
                        break;
                    case "bitAdr":
                        value = GetValue<T>(element.Attribute("bitAdr").Value);
                        map[logicalName] = value;
                        break;
                    default:
                        break;
                }
            }
            return map;
        }

        private static T GetValue<T>(string valueString)
        {
            if (typeof(T) == typeof(int))
            {
                int intValue = int.Parse(valueString);
                return (T)(object)intValue;
            }
            else if (typeof(T) == typeof(string))
            {
                return (T)(object)valueString;
            }
            else if (typeof(T) == typeof(byte))
            {
                byte byteValue = byte.Parse(valueString);
                return (T)(object)byteValue;
            }
            else
            {
                throw new NotSupportedException($"Unsupported type: {typeof(T)}");
            }
        }

        #endregion

        #region PLC的读写
        /// <summary>
        /// 根据地址名称和写入数据写入PLC
        /// </summary>
        /// <typeparam name="T">写入数据的类型(仅有bool和short两个类型)</typeparam>
        /// <param name="adrName">地址名称，在PLCAddress.config中</param>
        /// <param name="setInfo">写入数据(PLC的int类型传short类型)</param>
        /// <returns>是否写入成功</returns>
        public bool SetPLC<T>(string adrName, T setInfo)
        {
            try
            {
                PlcAddressMap.TryGetValue("DB_PLC", out var DB_PLCAddress);
                PlcAddressMap.TryGetValue(adrName, out var address);
                PlcBitAdrMap.TryGetValue(adrName, out var bitAdr);
                if (setInfo is bool)
                {
                    GlobalVariables.plc.Write(DataType.DataBlock, DB_PLCAddress, address, setInfo, bitAdr);
                }
                else if (setInfo is string)
                {
                    GlobalVariables.plc.Write(DataType.DataBlock, DB_PLCAddress, address, PLCConverter.ConvertStringToPLCString(Convert.ToString(setInfo)));
                }

                else if (setInfo is short)
                {
                    GlobalVariables.plc.Write(DataType.DataBlock, DB_PLCAddress, address, setInfo);
                }

                else
                {
                    GlobalVariables.plc.Write(DataType.DataBlock, DB_PLCAddress, address, setInfo);
                }
                IsGetPLCMessageOpen = false;
                return true;
            }
            catch (Exception ex)
            {
                IsGetPLCMessageOpen = true;
                Logger.Instance.Error(ex, $"设置PLC地址{adrName}异常");
                if (GlobalVariables.CallPolice == 0)
                {
                    GlobalVariables.CallPolice = (int)GlobalVariables.CallPoliceStatus.PLC连接失败;
                }
                return false;
            }
        }

        public object GetPLC(string adrName)
        {
            PlcAddressMap.TryGetValue("DB_PLC", out var DB_PLCAddress);
            PlcAddressMap.TryGetValue(adrName, out var address);
            PlcTypeMap.TryGetValue(adrName, out var type);
            PlcBitAdrMap.TryGetValue(adrName, out byte bitAdr);
            VarType varType = Converter.EnumTypeConverter<VarType>(type);
            object res;

            try
            {
                switch (type)
                {
                    case "Bit":
                        res = GlobalVariables.plc.Read(DataType.DataBlock, DB_PLCAddress, address, varType, 1, bitAdr);
                        break;
                    case "String":
                        res = GlobalVariables.plc.Read(DataType.DataBlock, DB_PLCAddress, address, varType, bitAdr);
                        res = PLCConverter.ConvertPLCStringToString((string)res);
                        break;

                    default:
                        res = GlobalVariables.plc.Read(DataType.DataBlock, DB_PLCAddress, address, varType, bitAdr);
                        break;
                }

                IsGetPLCMessageOpen = false;
                return res;
            }
            catch (Exception ex)
            {
                IsGetPLCMessageOpen = true;
                Logger.Instance.Error(ex, $"读取PLC地址{adrName}异常");
                if (GlobalVariables.CallPolice == 0)
                {
                    GlobalVariables.CallPolice = (int)GlobalVariables.CallPoliceStatus.PLC连接失败;
                }
                return null;
            }
        }

        #endregion

        public static async void Init()
        {
            try
            {
                GlobalVariables.plc.ReadTimeout = 3000;
                GlobalVariables.plc.WriteTimeout = 3000;
                //打开PLC
                if (!GlobalVariables.plc.IsConnected)
                {
                    await GlobalVariables.plc.OpenAsync().WaitAsync(TimeSpan.FromMilliseconds(3000));
                }
            }
            catch (TimeoutException ex)
            {
                Logger.Instance.Error(ex, "PLC1连接超时");
                MessageBox.Show("PLC1连接失败");
            }
            catch (Exception ex)
            {
                Logger.Instance.Error(ex, "PLC1连接失败");
                MessageBox.Show("PLC1连接失败");
            }
        }

    }
}
